#ifndef NAHIDATEST_H
#define NAHIDATEST_H

#include <iostream>
#include <stdexcept>
#include <functional>
#include <vector>

#define JUDGE_TIMEOUT(targetFunction, expectTime) do {float t = NahidaProject::NahidaUnitTest::CalculateExecuteTime(targetFunction); if ( t > expectTime) { throw std::runtime_error("Assertion failed: Execute time out.");} } while(0)
#define JUDGE_TRUE(condition) do {  if (!(condition)) { throw std::runtime_error("Assertion failed: " #condition);  } } while (0)
#define JUDGE_FALSE(condition) JUDGE_TRUE(!(condition))
#define JUDGE_EQUAL(expected, actual) do { if ((expected) != (actual)) { throw std::runtime_error("Assertion failed: " #expected " != " #actual); } } while (0)
#define JUDGE_NOTEQUAL(expected, actual) JUDGE_TRUE((expected) != (actual))
#define JUDGE_THROWEXCEPTION(statement, exception_type) do { bool bCaught = false; try { statement; } catch (const exception_type&) {  bCaught = true; } catch (...) { throw std::runtime_error("Assertion failed: Unexpected exception caught."); } if (!bCaught) { throw std::runtime_error("Assertion failed: Exception of type " #exception_type " not thrown."); } } while (0)

namespace NahidaProject {
	class __declspec(dllexport) NahidaUnitTest {
	public:
		static void RegisterTest(const std::string& testName, std::function<void()> testFunc);
		static void RunAllTests();
		static void RunTestByName(const std::string& testName);
		static float CalculateExecuteTime(std::function<void()> function);
	private:
		struct TestCase {std::string name; std::function<void()> func; };
		static std::vector<TestCase>& GetTests();
	};
}
#define TESTCASE(test_name) static void test_name(); struct TestRegister_##test_name { TestRegister_##test_name() { NahidaProject::NahidaUnitTest::RegisterTest(#test_name, test_name); } }; static TestRegister_##test_name g_register_##test_name; static void test_name()
#endif // NAHIDATEST_H